﻿using System;
using System.Collections.Generic;
using System.IO;

using DigitalQuran.Collection;
using DigitalQuran.IO;

namespace DigitalQuran
{
    /// <summary>
    /// Holy Qur'an Book
    /// </summary>
    public class Book : VerseSpan
    {
        public const string OrignalTextMode = "Original";

        /// <summary>
        /// <remarks>Constuctor is private to restrict instance creation. Only singlton instance can be craete using <see cref="DigitalQuran.Book.CreateInstance"/> </remarks>
        /// </summary>
        private Book() : base(1, VerseCollection.TotalVerses)
        {            
            Bowings = new BowingCollection();
            Chapters = new ChapterCollection();
            Groups = new GroupCollection();
            Pages = new PageCollection();
            Parts = new PartCollection();
            Prostration = new ProstrationCollection();
            Quarters = new QuarterCollection();
            Stations = new StationCollection();
            Words = new WordCollection();
        }

        /// <summary>
        /// Get Collection of all Bowings of Qur'an
        /// </summary>
        public BowingCollection Bowings { get; private set; }

        /// <summary>
        /// Get Collection of all Chapters of Qur'an
        /// </summary>
        public ChapterCollection Chapters { get; private set; }

        /// <summary>
        /// Get Collection of all Groups of Qur'an
        /// </summary>
        public GroupCollection Groups { get; private set; }

        /// <summary>
        /// Get Collection of all Pages of Qur'an
        /// </summary>
        public PageCollection Pages { get; private set; }

        /// <summary>
        /// Get Collection of all Parts of Qur'an
        /// </summary>
        public PartCollection Parts { get; private set; }

        /// <summary>
        /// Get Collection of all Prostration of Qur'an
        /// </summary>
        public ProstrationCollection Prostration { get; private set; }

        /// <summary>
        /// Get Collection of all Quarters of Qur'an
        /// </summary>
        public QuarterCollection Quarters { get; private set; }

        /// <summary>
        /// Get Collection of all Stations of Qur'an
        /// </summary>
        public StationCollection Stations { get; private set; }

        /// <summary>
        /// Get Collection of all Words of Qur'an
        /// </summary>
        public WordCollection Words { get; private set; }

        /// <summary>
        /// Creates new instance of <see cref="DigitalQuran.Book"/>
        /// </summary>
        /// <returns>Instance of <see cref="DigitalQuran.Book"/></returns>
        public static Book CreateInstance()
        {
            _Instance = new Book();

            VerseCollection.LoadData(PrivateStorage.ReadVerses(DigitalQuranDirectories.Data + "quran-uthmani.bin"));
            PrivateStorage.LoadQuranObjectsFromMetadata();
            _Instance.UpdateNumbers();

            WordCollection.LoadWordRoots();
            WordCollection.LoadWordMeanings();

            if (Globals.Edition == Edition.Grammar)
                WordCollection.LoadWordParts();

            return _Instance;
        }

        static Book _Instance = null;
        /// <summary>
        /// Gets the singlton instance of <see cref="DigitalQuran.Book"/>
        /// <remarks>Object Instance will be craeted if <see cref="DigitalQuran.Book.CreateInstance"/> is not called yet</remarks>
        /// </summary>
        public static Book Instance
        {
            get
            {
                if (_Instance == null)
                    CreateInstance();

                return _Instance;
            }
        }

        void UpdateNumbers()
        {
            int letterNo = 1;
            int letterNumberInWord, letterNumberInVerse, wordNumberInVerse, letterNumberInChapter, wordNumberInChapter, verseNumberInChapter;

            foreach (var chapter in Book.Instance.Chapters)
            {
                verseNumberInChapter = 1;
                wordNumberInChapter = 1;
                letterNumberInChapter = 1;

                foreach (Verse verse in chapter.Verses)
                {
                    verse.NumberInChapter = verseNumberInChapter++;

                    wordNumberInVerse = 1;
                    letterNumberInVerse = 1;

                    foreach (Word word in verse.Words)
                    {
                        word.NumberInChapter = wordNumberInChapter++;
                        word.NumberInVerse = wordNumberInVerse++;

                        letterNumberInWord = 1;
                        foreach (Letter letter in word.Letters)
                        {
                            letter.GlobalNumber = letterNo++;
                            letter.NumberInChapter = letterNumberInChapter++;
                            letter.NumberInVerse = letterNumberInVerse++;
                            letter.NumberInWord = letterNumberInWord++;
                        }
                    }
                }
            }            
        }

        Dictionary<string, int[]> _QuranRootWords = null;
        /// <summary>
        /// Gets the Qur'an Root Word Dictionary
        /// </summary>
        public Dictionary<string, int[]> QuranRootWords
        {
            get
            {
                if (_QuranRootWords == null)
                    LoadQuranRootWords();

                return _QuranRootWords;
            }
        }

        /// <summary>
        /// Read and load Uthmani Qur'an Root Words
        /// </summary>
        void LoadQuranRootWords()
        {
            string filename = DigitalQuranDirectories.Data + "quran-uthmani-root-words.bin";
            _QuranRootWords = new Dictionary<string, int[]>();

            if (File.Exists(filename))
            {
                using (var stream = new FileStream(filename, FileMode.Open))
                using (var reader = new BinaryReader(stream))
                {
                    string key;
                    int count;

                    for (int i = reader.ReadInt32(); i > 0; i--)
                    {
                        key = reader.ReadString();
                        count = reader.ReadInt32();

                        int[] rootWords = new int[count];

                        for (int j = 0; j < count; j++)
                            rootWords[j] = reader.ReadInt32();

                        _QuranRootWords.Add(key, rootWords);
                    }
                }
            }
        }

        public override int GetCount(QuranObjectType objType)
        {
            switch (objType)
            {
                case QuranObjectType.Bowing:
                    return Bowings.Count;
                case QuranObjectType.Chapter:
                    return Chapters.Count;
                case QuranObjectType.Group:
                    return Groups.Count;
                case QuranObjectType.Page:
                    return Pages.Count;
                case QuranObjectType.Part:
                    return Parts.Count;
                case QuranObjectType.Prostration:
                    return Prostration.Count;
                case QuranObjectType.Quarter:
                    return Quarters.Count;
                case QuranObjectType.Station:
                    return Stations.Count;                    
            }

            return base.GetCount(objType);
        }

        /// <inheritdoc />
        public override IQuranObject GetObject(QuranObjectType objType, int index)
        {
            switch (objType)
            {
                case QuranObjectType.Bowing:
                    return Bowings[index];
                case QuranObjectType.Chapter:
                    return Chapters[index];
                case QuranObjectType.Group:
                    return Groups[index];
                case QuranObjectType.Page:
                    return Pages[index];
                case QuranObjectType.Part:
                    return Parts[index];
                case QuranObjectType.Prostration:
                    return Prostration[index];
                case QuranObjectType.Quarter:
                    return Quarters[index];
                case QuranObjectType.Station:
                    return Stations[index];
            }

            return base.GetObject(objType, index);
        }
    }
}